Skip to content

feat(efcore): Weasel model → EF MigrationOperation translation layer - #375

Merged
jeremydmiller merged 1 commit into
masterfrom
feat/ef-migration-operations
Jul 18, 2026
Merged

feat(efcore): Weasel model → EF MigrationOperation translation layer#375
jeremydmiller merged 1 commit into
masterfrom
feat/ef-migration-operations

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Closes #365. First implementation phase of the EF Core migration generation epic (#371), building directly on the verified #364 spike results (attribute-only migrations with empty target models are first-class in the EF toolchain).

What's here

New MigrationOperationTranslation in Weasel.EntityFrameworkCore — the reverse direction of MapToTable: walk Weasel's schema model and produce EF Core MigrationOperation instances, the intermediate representation the C# migration file emitter (#366) will render as MigrationBuilder calls.

  • Input is the provider-neutral surface (ITable, ITableColumn, ITableIndex, ForeignKeyBase, SequenceBase) — the project keeps zero concrete-provider references. Two small additive changes to Weasel.Core expose what was missing: ITable.Columns and ITableIndex.Columns (implicitly satisfied by every provider's existing concrete members).
  • Raw store types everywhere: every column carries ColumnType so EF's CLR type mapping is bypassed and the resulting DDL matches Weasel's own exactly. The ClrType is a best-effort inverse map used only for the table.Column<T>(...) generic argument in emitted C#.
  • Structure: CreateTable with nested columns / primary key / check constraints / foreign keys, one CreateIndex per index, EnsureSchema per non-default schema (deduplicated; public/dbo become null Schema, matching EF's own scaffolding), CreateSequence from SequenceBase (StartWith/IncrementBy for HiLo).
  • Provider mapping (per the issue's table): identity → Npgsql:ValueGenerationStrategy / SqlServer:Identity annotations; computed columns → ComputedColumnSql + IsStored (always stored on PostgreSQL); index filter → first-class Filter, includes/method → provider annotations; CascadeActionReferentialAction with SQL Server RestrictNoAction, mirroring the mapDeleteBehavior normalization from the EF→Weasel direction.
  • Raw-SQL fallback: everything EF cannot model routes through SqlOperation carrying the object's own WriteCreateStatement output — non-table/non-sequence schema objects (functions, sprocs, table types) automatically, plus anything matched by the ForceRawSql hook (partitioned tables being the canonical case; callers with provider references downcast there, same pattern as the harness's customizeTables). Expression indexes throw NotSupportedException with guidance to use the hook.
  • ToDropMigrationOperations builds Down() bodies: reverse-order DropTable/DropSequence/raw drops. Schemas are deliberately never dropped (they may be shared with Marten/Wolverine).
  • Npgsql identity annotation value is the string "IdentityByDefaultColumn" in-memory (this project can't reference the provider enum); the EF migration generation: C# migration file emitter + stub DbContext #366 emitter renders it as the proper NpgsqlValueGenerationStrategy literal, and callers feeding operations directly to the Npgsql SQL generator can overwrite NpgsqlIdentityAnnotationValue with the real enum.

Renames (RenameColumnOperation/RenameTableOperation) belong to the incremental TableDelta phase (#367) per the issue's design note.

Testing

13 new DB-free unit tests covering: full PG table shape (raw types, nullability, defaults, identity annotation, PK, checks), SQL Server identity, computed columns both providers, index filter/includes/method, expression-index rejection, FK referential actions incl. the Restrict normalization split, default-schema omission, sequence translation, ForceRawSql partitioned-table fallback, missing-Migrator error, drop ordering, and PreserveIdentifierCase flow-through.

End-to-end validation (operations → SQL → schema comparison) deliberately belongs to #369. Full local suites green: Core 21, SQLite 361, PostgreSQL 767, SQL Server 302, EF Core (PG+SS) 75.

🤖 Generated with Claude Code

Closes #365. First implementation phase of the EF Core migration
generation epic (#371), building on the #364 spike results.

- New MigrationOperationTranslation in Weasel.EntityFrameworkCore: walks
  the provider-neutral surface (ITable/ITableColumn/ITableIndex/
  ForeignKeyBase/SequenceBase) and produces EF Core MigrationOperation
  instances — the reverse of MapToTable. Raw store type strings
  (ColumnType) everywhere so EF's CLR mapping is bypassed and DDL matches
  Weasel exactly; the CLR type is a best-effort inverse used only for the
  Column<T>() generic in emitted C#
- CreateTable with nested columns / primary key / check constraints /
  foreign keys, one CreateIndex per index, EnsureSchema per non-default
  schema (deduplicated; default public/dbo emitted as null Schema like
  EF's own scaffolding), CreateSequence from SequenceBase
- Provider specifics: identity → Npgsql:ValueGenerationStrategy or
  SqlServer:Identity annotations; computed columns → ComputedColumnSql +
  IsStored (always stored on PG); index includes/method annotations;
  CascadeAction → ReferentialAction with SQL Server Restrict ≡ NoAction
  mirroring mapDeleteBehavior
- Raw-SQL fallback: non-table/non-sequence objects (functions, sprocs,
  table types) and anything matched by the ForceRawSql hook (e.g.
  partitioned tables) are wrapped in SqlOperation carrying the object's
  own WriteCreateStatement DDL; expression indexes throw with guidance
  to use the hook
- ToDropMigrationOperations for Down() bodies: reverse-order DropTable /
  DropSequence / raw drops; schemas never dropped (may be shared with
  Marten/Wolverine)
- Weasel.Core additions: ITable.Columns and ITableIndex.Columns expose
  the column collections on the neutral surface (implicitly satisfied by
  every provider's concrete types)

13 new DB-free unit tests; all provider suites green locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EF migration generation: Weasel model → MigrationBuilder operation translation layer

1 participant